package com.stars.easyms.redis.interceptor;

import com.alibaba.fastjson.JSON;
import com.stars.easyms.base.util.GenericTypeUtil;
import com.stars.easyms.base.util.ReflectUtil;
import com.stars.easyms.base.util.TimeUtil;
import com.stars.easyms.redis.annotion.RedisListRange;
import com.stars.easyms.redis.constant.EasyMsRedisConstants;
import com.stars.easyms.redis.exception.RedisInterceptorException;
import com.stars.easyms.redis.exception.RedisRuntimeException;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>className: RedisListRangeInterceptor</p>
 * <p>description: RedisListRange注解拦截器</p>
 *
 * @author
 * @date 2019-12-24 17:44
 */
public class RedisListRangeInterceptor extends AbstractRedisInterceptor<RedisListRange> {

    @Override
    Object invoke(MethodInvocation methodInvocation, RedisListRange redisListRange) throws RedisInterceptorException {
        Object result = null;
        if(isEnabled()){
            Method pointMethod = methodInvocation.getMethod();
            String methodFullName = ReflectUtil.getMethodFullName(methodInvocation.getMethod());
            Class<?> returnType = pointMethod.getReturnType();
            Type genericReturnType = pointMethod.getGenericReturnType();
            boolean isInvalidReturnType = !List.class.isAssignableFrom(returnType);
            if (isInvalidReturnType) {
                logger.error("Method '{}' returnType should be List<T>!", ReflectUtil.getMethodFullName(pointMethod));
            }
            String redisKey = getRedisKey(methodInvocation, redisListRange.group());
            Map<String, Long> redisIndexList = getRedisIndexStartAndEnd(methodInvocation);
            Long redisIndexStart = redisIndexList.get("redisIndexStart");
            Long redisIndexEnd = redisIndexList.get("redisIndexEnd");
            boolean override = redisListRange.override();
            if(redisIndexStart > redisIndexEnd){
                throw new RedisRuntimeException("Method '{}' RedisIndexStart({}) can't be bigger than RedisIndexEnd({})!", methodFullName, redisIndexStart, redisIndexEnd);
            }

            if (!(genericReturnType instanceof ParameterizedType)) {
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd);
            } else {
                Class clazz = GenericTypeUtil.getGenericClass(genericReturnType, 0);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd, clazz);
            }
            if(!CollectionUtils.isEmpty((List)result)){
                /**
                 * 去除结果集合为true
                 */
                if(override){
                    easyMsRedisTemplate.leftTrim(redisKey, redisIndexStart, redisIndexEnd);
                }
                Long expire = TimeUtil.parseToMilliseconds(redisListRange.expire());
                boolean isSetExpire = expire != null && expire > 0 && easyMsRedisTemplate.getExpire(redisKey, TimeUnit.MILLISECONDS) <= 0;
                if (isSetExpire) {
                    easyMsRedisTemplate.expire(redisKey, expire, TimeUnit.MILLISECONDS);
                }
                return result;
            }
            /**
             * List存在区间获取的 如果结果值不存在  则需要判断缓存中是否redisKey
             * 不存在 则进行方法调用
             */
            boolean existsFlag =  easyMsRedisTemplate.hasKey(redisKey);
            if(!existsFlag){
                /**结果不存在的时候*/
                result = proceed(methodInvocation);
                if(null != result){
                    String type = redisListRange.type();
                    Long expire = getExpire(methodInvocation);
                    expire = expire != null ? expire : TimeUtil.parseToMilliseconds(redisListRange.expire());
                    result = dealMethodResultList(result, returnType, type, redisKey, genericReturnType,
                            redisIndexStart, redisIndexEnd, expire, override);
                }
            }
        }
        return result;
    }

    /**
     * 处理执行方法之后的缓存方式以及获取区间值
     * @param result 返回结果
     * @param returnType 返回类型
     * @param type 存储方式（L or R）
     * @param redisKey 缓存Key
     * @param genericReturnType 返回类型
     * @param redisIndexStart 查询开始区间
     * @param redisIndexEnd 查询结束区间
     * @param expire  有效期
     * @param override 是否覆盖
     * @return
     */
    private Object dealMethodResultList(Object result, Class<?> returnType,
                                  String type, String redisKey, Type genericReturnType,
                                  Long redisIndexStart, Long redisIndexEnd, Long expire,
                                  Boolean override){
        /**
         * type L:代表是从左存入到LIST中
         *      R:代表是从右边存到LIST中
         */
        if(EasyMsRedisConstants.LIST_TYPE_LEFT.equals(type)) {
            if(List.class.isAssignableFrom(returnType)&& String.class.isAssignableFrom(GenericTypeUtil.getGenericClass(genericReturnType, 0))){
                easyMsRedisTemplate.lpushAll(redisKey, (List<String>) result);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd);
            }else if(List.class.isAssignableFrom(returnType)&& !String.class.isAssignableFrom(GenericTypeUtil.getGenericClass(genericReturnType, 0))){
                List<String> strResultList = new ArrayList<>();
                ((List<?>) result).forEach(t -> strResultList.add(JSON.toJSONString(t)));
                easyMsRedisTemplate.lpushAll(redisKey, strResultList);
                Class clazz = GenericTypeUtil.getGenericClass(genericReturnType, 0);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd, clazz);
            }else{
                easyMsRedisTemplate.lpush(redisKey, result);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd);
            }
        }else if(EasyMsRedisConstants.LIST_TYPE_RIGHT.equals(type)){
            if(List.class.isAssignableFrom(returnType)&& String.class.isAssignableFrom(GenericTypeUtil.getGenericClass(genericReturnType, 0))){
                easyMsRedisTemplate.rpushAll(redisKey, (List<String>) result);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd);
            }else if(List.class.isAssignableFrom(returnType)&& !String.class.isAssignableFrom(GenericTypeUtil.getGenericClass(genericReturnType, 0))){
                List<String> strResultList = new ArrayList<>();
                ((List<Object>) result).forEach(t -> strResultList.add(JSON.toJSONString(t)));
                easyMsRedisTemplate.rpushAll(redisKey, strResultList);
                Class clazz = GenericTypeUtil.getGenericClass(genericReturnType, 0);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd, clazz);
            }else{
                easyMsRedisTemplate.rpush(redisKey, result);
                result = easyMsRedisTemplate.leftrange(redisKey, redisIndexStart, redisIndexEnd);
            }
        }
        if(override){
            easyMsRedisTemplate.leftTrim(redisKey, redisIndexStart, redisIndexEnd);
        }
        boolean isSetExpire = expire != null && expire > 0;
        if (isSetExpire) {
            easyMsRedisTemplate.expire(redisKey, expire, TimeUnit.MILLISECONDS);
        }
        return result;
    }
}